package com.googlecode.quillen.application;

import com.googlecode.quillen.domain.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.lang.StringUtils;
import static com.googlecode.quillen.util.Utils.logInfo;
import static com.googlecode.quillen.util.Utils.logError;
import com.googlecode.quillen.util.*;
import com.googlecode.quillen.service.ChunkService;
import com.googlecode.quillen.service.ShadowFileService;
import com.googlecode.quillen.service.SnapshotService;

import java.io.IOException;
import java.util.*;
import java.text.ParseException;

/**
 * Created by IntelliJ IDEA.
 * User: greg
 * Date: Dec 18, 2008
 * Time: 2:21:58 AM
 */
public class AdminImpl implements Admin
{
    private static final Log LOG = LogFactory.getLog(AdminImpl.class);

    private final ChunkService chunkService;
    private final ShadowFileService shadowFileService;
    private final SnapshotService snapshotService;

    public AdminImpl(ChunkService chunkService, ShadowFileService shadowFileService, SnapshotService snapshotService)
    {
        this.chunkService = chunkService;
        this.shadowFileService = shadowFileService;
        this.snapshotService = snapshotService;
    }

    public void install() throws AttributeStorageException, ObjectStorageException, WorkQueueAbortedException
    {
        WorkQueue workQueue = new WorkQueue(new ExceptionHandler()
        {
            public boolean handleException(Exception ex)
            {
                logError(LOG, ex, null, "error while installing object buckets and attribute databases");

                return true;
            }
        });

        workQueue.enqueue(new WorkItem()
        {
            public void run() throws Exception
            {
                chunkService.createStorage();
            }
        });

        workQueue.enqueue(new WorkItem()
        {
            public void run() throws Exception
            {
                shadowFileService.createStorage();
            }
        });

        workQueue.enqueue(new WorkItem()
        {
            public void run() throws Exception
            {
                snapshotService.createStorage();
            }
        });

        workQueue.freezeAndBlockUntilEmpty();

        logInfo(LOG, "installed object buckets and attribute databases");
    }

    public void uninstall() throws AttributeStorageException, ObjectStorageException, WorkQueueAbortedException
    {
        WorkQueue workQueue = new WorkQueue(new ExceptionHandler()
        {
            public boolean handleException(Exception ex)
            {
                logError(LOG, ex, null, "error while uninstalling object buckets and attribute databases");

                return true;
            }
        });

        workQueue.enqueue(new WorkItem()
        {
            public void run() throws Exception
            {
                chunkService.deleteStorage();
            }
        });

        workQueue.enqueue(new WorkItem()
        {
            public void run() throws Exception
            {
                shadowFileService.deleteStorage();
            }
        });

        workQueue.enqueue(new WorkItem()
        {
            public void run() throws Exception
            {
                snapshotService.deleteStorage();
            }
        });

        workQueue.freezeAndBlockUntilEmpty();

        logInfo(LOG, "uninstalled object buckets and attribute databases");
    }

    public void listSnapshots(ResultConsumer<Snapshot> consumer) throws AttributeStorageException, ParseException
    {
        snapshotService.list(consumer);
    }

    public void listSnapshots(String prefix, ResultConsumer<Snapshot> consumer) throws AttributeStorageException, ParseException
    {
        snapshotService.find(prefix, consumer);
    }

    public void listSnapshots(String prefix, Date maxDate, ResultConsumer<Snapshot> consumer) throws AttributeStorageException, ParseException
    {
        snapshotService.find(prefix, maxDate, consumer);
    }

    // TODO: sort and limit results
    public void listSnapshotContents(final Collection<String> snapshots, final String filenamePrefix, final ResultConsumer<FileInfo> consumer) throws AttributeStorageException, ParseException
    {
        for (final String snapshot : snapshots)
        {
            shadowFileService.findStubs(snapshot, filenamePrefix, new ResultConsumer<ShadowFile>()
            {
                public void newResult(ShadowFile result) throws Exception
                {
                    if (consumer != null)
                    {
                        for (String filename : result.getFilenames())
                        {
                            consumer.newResult(new FileInfo(snapshot, filename, result.getSize(), result.getDate()));
                        }
                    }
                }
            });
        }
    }

    public void deleteSnapshots(final Collection<String> snapshots, final ResultConsumer<FileInfo> consumer)
            throws AttributeStorageException, ObjectStorageException, IOException, WorkQueueAbortedException, ParseException
    {
        final WorkQueue workQueue = new WorkQueue(10, 50, new ExceptionHandler()
        {
            public boolean handleException(Exception ex)
            {
                logError(LOG, ex, consumer, "error while deleting snapshots %s", StringUtils.join(snapshots, ", "));

                return true;
            }
        });

        final MutexProvider mutexProvider = new MutexProvider();

        try
        {
            for (final String snapshot : snapshots)
            {
                snapshotService.delete(snapshot);

                shadowFileService.findStubs(snapshot, null, new ResultConsumer<ShadowFile>()
                {
                    public void newResult(final ShadowFile result) throws Exception
                    {
                        workQueue.enqueue(new WorkItem()
                        {
                            public void run() throws Exception
                            {
                                synchronized (mutexProvider.getMutex(result.getKey()))
                                {
                                    ShadowFile shadowFile = shadowFileService.delete(result.getKey(), Arrays.asList(snapshot));

                                    if (shadowFile != null)
                                    {
                                        for (String chunkKey : shadowFile.getChunkKeys())
                                        {
                                            // TODO: delete chunks on another work queue
                                            chunkService.delete(chunkKey, Arrays.asList(shadowFile.getKey()));
                                        }
                                    }
                                }

                                for (String filename : result.getFilenames())
                                {
                                    logInfo(LOG, "deleted file %s from snapshot %s", filename, snapshot);

                                    if (consumer != null)
                                    {
                                        consumer.newResult(new FileInfo(snapshot, filename, result.getSize(), result.getDate()));
                                    }
                                }
                            }
                        });
                    }
                });
            }

            workQueue.freezeAndBlockUntilEmpty();

            logInfo(LOG, "deleted %d snapshots: %s", snapshots.size(), StringUtils.join(snapshots, ", "));
        }
        finally
        {
            if (!workQueue.isEmpty())
            {
                workQueue.abort();
            }
        }
    }

    public void deleteSnapshots(String prefix, ResultConsumer<FileInfo> consumer) throws AttributeStorageException, IOException, ObjectStorageException, WorkQueueAbortedException, ParseException
    {
        SnapshotNameAccumulator snapshotNameAccumulator = new SnapshotNameAccumulator();

        snapshotService.find(prefix, snapshotNameAccumulator);

        deleteSnapshots(snapshotNameAccumulator.names, consumer);
    }

    public void deleteSnapshots(String prefix, Date maxDate, ResultConsumer<FileInfo> consumer) throws AttributeStorageException, IOException, ObjectStorageException, WorkQueueAbortedException, ParseException
    {
        SnapshotNameAccumulator snapshotNameAccumulator = new SnapshotNameAccumulator();

        snapshotService.find(prefix, maxDate, snapshotNameAccumulator);

        deleteSnapshots(snapshotNameAccumulator.names, consumer);
    }

    private class SnapshotNameAccumulator extends ResultConsumer<Snapshot>
    {
        public ArrayList<String> names = new ArrayList<String>();

        public void newResult(Snapshot result) throws Exception
        {
            names.add(result.getName());
        }
    }
}
